using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using AiPlatformAdmin.Api.Data;
using AiPlatformAdmin.Api.Models;

namespace AiPlatformAdmin.Api.Services;

public class DatabaseInitializer
{
    private readonly AppDbContext _context;
    private readonly ILogger<DatabaseInitializer> _logger;
    private readonly IConfiguration _configuration;

    public DatabaseInitializer(
        AppDbContext context,
        ILogger<DatabaseInitializer> logger,
        IConfiguration configuration)
    {
        _context = context;
        _logger = logger;
        _configuration = configuration;
    }

    /// <summary>
    /// Seeds initial data into the database (tenant, users, teams)
    /// </summary>
    public async Task SeedDataAsync()
    {
        try
        {
            _logger.LogInformation("Starting database seed data initialization...");

            var defaultTenantKey = _configuration["Database:DefaultTenant:TenantKey"] ?? "default";
            var defaultTenantName = _configuration["Database:DefaultTenant:Name"] ?? "Default Organization";
            var defaultTenantDescription = _configuration["Database:DefaultTenant:Description"] ?? "Default tenant for the system";

            var defaultTenant = await EnsureDefaultTenantAsync(defaultTenantKey, defaultTenantName, defaultTenantDescription);

            await EnsureUsersHaveTenantAsync(defaultTenant.Id);

            var adminUsername = Environment.GetEnvironmentVariable("ADMIN_USERNAME")
                ?? _configuration["Database:AdminUser:Username"]
                ?? "admin";
            var adminEmail = Environment.GetEnvironmentVariable("ADMIN_EMAIL")
                ?? _configuration["Database:AdminUser:Email"]
                ?? "admin@aiplatform.local";
            var adminPassword = Environment.GetEnvironmentVariable("ADMIN_PASSWORD")
                ?? _configuration["Database:AdminUser:Password"]
                ?? "Admin@123456";

            var adminUser = await EnsureUserAsync(
                username: adminUsername,
                email: adminEmail,
                password: adminPassword,
                tenantId: defaultTenant.Id,
                roles: new[] { UserRole.SystemAdministrator, UserRole.Administrator, UserRole.PlatformUser });

            var platformUsername = Environment.GetEnvironmentVariable("PLATFORM_USER_USERNAME")
                ?? _configuration["Database:PlatformUser:Username"]
                ?? "user";
            var platformEmail = Environment.GetEnvironmentVariable("PLATFORM_USER_EMAIL")
                ?? _configuration["Database:PlatformUser:Email"]
                ?? "user@aiplatform.local";
            var platformPassword = Environment.GetEnvironmentVariable("PLATFORM_USER_PASSWORD")
                ?? _configuration["Database:PlatformUser:Password"]
                ?? "User@123456";

            var platformUser = await EnsureUserAsync(
                username: platformUsername,
                email: platformEmail,
                password: platformPassword,
                tenantId: defaultTenant.Id,
                roles: new[] { UserRole.PlatformUser });

            var defaultTeamName = _configuration["Database:DefaultTeam:Name"] ?? "Default Team";
            var defaultTeamDescription = _configuration["Database:DefaultTeam:Description"] ?? "Default team for the organization";

            var defaultTeam = await EnsureDefaultTeamAsync(defaultTenant.Id, defaultTeamName, defaultTeamDescription);

            await EnsureTeamMembershipAsync(defaultTeam.Id, adminUser.Id, setAdmin: true);
            await EnsureTeamMembershipAsync(defaultTeam.Id, platformUser.Id, setAdmin: false);

            _logger.LogInformation("Database seed data initialization completed successfully!");
            _logger.LogInformation("=================================================");
            _logger.LogInformation("DEFAULT CREDENTIALS:");
            _logger.LogInformation($"Admin Username: {adminUsername}");
            _logger.LogInformation($"Admin Email: {adminEmail}");
            _logger.LogInformation($"Admin Password: {adminPassword}");
            _logger.LogInformation($"Admin Roles: SystemAdministrator, Administrator, PlatformUser");
            _logger.LogInformation("-------------------------------------------------");
            _logger.LogInformation($"Platform User Username: {platformUsername}");
            _logger.LogInformation($"Platform User Email: {platformEmail}");
            _logger.LogInformation($"Platform User Password: {platformPassword}");
            _logger.LogInformation($"Platform User Roles: PlatformUser");
            _logger.LogInformation("=================================================");
            _logger.LogWarning("IMPORTANT: Please change these default passwords immediately!");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An error occurred while seeding database data.");
            throw;
        }
    }

    private async Task<Tenant> EnsureDefaultTenantAsync(string tenantKey, string name, string description)
    {
        var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantKey == tenantKey);

        if (tenant == null)
        {
            tenant = new Tenant
            {
                TenantKey = tenantKey,
                Name = name,
                Description = description,
                IsActive = true,
                CreatedAt = DateTime.UtcNow,
                UpdatedAt = DateTime.UtcNow
            };

            _context.Tenants.Add(tenant);
            await _context.SaveChangesAsync();
            _logger.LogInformation($"Created default tenant: {tenant.Name} (ID: {tenant.Id})");
        }
        else
        {
            var hasChanges = false;

            if (!string.Equals(tenant.Name, name, StringComparison.Ordinal))
            {
                tenant.Name = name;
                hasChanges = true;
            }

            if (!string.Equals(tenant.Description ?? string.Empty, description, StringComparison.Ordinal))
            {
                tenant.Description = description;
                hasChanges = true;
            }

            if (!tenant.IsActive)
            {
                tenant.IsActive = true;
                hasChanges = true;
            }

            if (hasChanges)
            {
                tenant.UpdatedAt = DateTime.UtcNow;
                await _context.SaveChangesAsync();
                _logger.LogInformation($"Updated default tenant metadata (ID: {tenant.Id}).");
            }
            else
            {
                _logger.LogInformation($"Default tenant already exists (ID: {tenant.Id}).");
            }
        }

        return tenant;
    }

    private async Task<User> EnsureUserAsync(string username, string email, string password, int tenantId, IEnumerable<UserRole> roles)
    {
        var user = await _context.Users.FirstOrDefaultAsync(u => u.Username == username || u.Email == email);

        if (user == null)
        {
            user = new User
            {
                Username = username,
                Email = email,
                PasswordHash = BCrypt.Net.BCrypt.HashPassword(password),
                TenantId = tenantId,
                CreatedAt = DateTime.UtcNow,
                UpdatedAt = DateTime.UtcNow
            };
            user.SetRoles(roles);

            _context.Users.Add(user);
            await _context.SaveChangesAsync();
            _logger.LogInformation($"Created user '{username}' (ID: {user.Id}) with roles: {string.Join(", ", user.GetRoles())}.");
        }
        else
        {
            var mutated = false;

            if (!string.Equals(user.Email, email, StringComparison.OrdinalIgnoreCase))
            {
                user.Email = email;
                mutated = true;
            }

            if (user.TenantId != tenantId)
            {
                user.TenantId = tenantId;
                mutated = true;
            }

            var desiredRoles = roles.Distinct().OrderBy(r => r).ToList();
            var existingRoles = user.GetRoles().OrderBy(r => r).ToList();
            if (!desiredRoles.SequenceEqual(existingRoles))
            {
                user.SetRoles(desiredRoles);
                mutated = true;
            }

            if (mutated)
            {
                user.UpdatedAt = DateTime.UtcNow;
                await _context.SaveChangesAsync();
                _logger.LogInformation($"Updated user '{username}' metadata (ID: {user.Id}).");
            }
        }

        return user;
    }

    private async Task<Team> EnsureDefaultTeamAsync(int tenantId, string teamName, string description)
    {
        var team = await _context.Teams.FirstOrDefaultAsync(t => t.TenantId == tenantId && t.Name == teamName);

        if (team == null)
        {
            team = new Team
            {
                TenantId = tenantId,
                Name = teamName,
                Description = description,
                CreatedAt = DateTime.UtcNow,
                UpdatedAt = DateTime.UtcNow
            };

            _context.Teams.Add(team);
            await _context.SaveChangesAsync();
            _logger.LogInformation($"Created default team '{teamName}' (ID: {team.Id}).");
        }
        else if (!string.Equals(team.Description ?? string.Empty, description, StringComparison.Ordinal))
        {
            team.Description = description;
            team.UpdatedAt = DateTime.UtcNow;
            await _context.SaveChangesAsync();
            _logger.LogInformation($"Updated default team description (ID: {team.Id}).");
        }

        return team;
    }

    private async Task EnsureTeamMembershipAsync(int teamId, int userId, bool setAdmin)
    {
        var changes = false;

        if (!await _context.UserTeams.AnyAsync(ut => ut.TeamId == teamId && ut.UserId == userId))
        {
            _context.UserTeams.Add(new UserTeam
            {
                TeamId = teamId,
                UserId = userId
            });
            changes = true;
            _logger.LogInformation($"Added user {userId} to team {teamId}.");
        }

        if (setAdmin && !await _context.TeamAdmins.AnyAsync(ta => ta.TeamId == teamId && ta.UserId == userId))
        {
            _context.TeamAdmins.Add(new TeamAdmin
            {
                TeamId = teamId,
                UserId = userId,
                CreatedAt = DateTime.UtcNow
            });
            changes = true;
            _logger.LogInformation($"Granted team admin to user {userId} for team {teamId}.");
        }

        if (changes)
        {
            await _context.SaveChangesAsync();
        }
    }

    private async Task EnsureUsersHaveTenantAsync(int defaultTenantId)
    {
        var validTenantIds = await _context.Tenants.Select(t => t.Id).ToListAsync();
        var orphanUsers = await _context.Users
            .Where(u => u.TenantId <= 0 || !validTenantIds.Contains(u.TenantId))
            .ToListAsync();

        if (orphanUsers.Count == 0)
        {
            return;
        }

        foreach (var user in orphanUsers)
        {
            user.TenantId = defaultTenantId;
            user.UpdatedAt = DateTime.UtcNow;
        }

        await _context.SaveChangesAsync();
        _logger.LogInformation($"Reassigned {orphanUsers.Count} user(s) to default tenant (ID: {defaultTenantId}).");
    }
}
